package com.service;

import com.entity.Task;
import com.mapper.AccountMapper;
import com.mapper.AccountXpInfoMapper;
import com.mapper.TaskMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;

//import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle.details;


/**
 * ClassName: TaskMonitorService
 * Description:
 *
 * @author L_Z_R
 * @version 1.0
 * @create 2025/05/26 09:50
 */
@Service
public class TaskMonitorService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private AccountXpInfoMapper accountXpInfoMapper;

    public static final int PRODUCT_EXPERIENCE_OFFICER = 260; // 产品体验官所需的评估分数
    public static final int MODERATOR = 310; // 版主所需的评估分数
    //任务类型映射
    private static final Map<Byte, String> TASK_KEY_MAP = Map.of(
            (byte) 1, "sign_in",
            (byte) 2, "post",
            (byte) 3, "comment",
            (byte) 4, "like"
    );

    // 任务类型对应的活跃度分数
    private static final Map<Byte, Integer> TASK_SCORE_MAP = Map.of(
            (byte) 1, 5,   // 签到
            (byte) 2, 10,  // 发帖
            (byte) 3, 3,   // 评论
            (byte) 4, 1    // 点赞
    );

    // 活跃度相关Redis Key
    private static final String ACTIVITY_SCORE_KEY = "user:%d:activity:scores"; // 活跃度分数
    private static final String ACTIVITY_STREAK_KEY = "user:%d:activity:streak"; // 连续活跃天数
    private static final String ACTIVITY_HISTORY_KEY = "user:%d:activity:history:%s"; // 活跃度历史

    /**
     * 监控任务完成状态
     *
     * @param accountId 用户Id
     * @param taskType  任务类型
     */
    public void checkAndRewardTask(Integer accountId, byte taskType) {
        String taskKey = TASK_KEY_MAP.get(taskType);
        String dailyKey = "user:" + accountId + ":tasks:" + LocalDate.now();
        String monitorKey = "user:" + accountId + ":monitor";
        String rewardKey = "user:" + accountId + ":rewards:" + LocalDate.now();

        // 如果今日已完成所有任务，直接返回
        if (redisTemplate.hasKey(monitorKey)) {
            return;
        }

        // 如果该任务已领过奖励，直接返回
        if (redisTemplate.opsForHash().hasKey(rewardKey, taskKey)) {
            return;
        }

        // 获取并更新任务计数
        Integer currentCount = (Integer) redisTemplate.opsForHash().get(dailyKey, taskKey);
        if (currentCount == null) {
            currentCount = 0;
        }
        redisTemplate.opsForHash().put(dailyKey, taskKey, currentCount + 1);

        // 获取任务配置
        Task task = taskMapper.selectTaskConfigByType(taskType);
        if (task == null) {
            return;
        }

        // 检查是否达到要求次数
        if (currentCount + 1 >= task.getTaskTime()) {
            // 发放奖励
            accountXpInfoMapper.updateAccountXpById(accountId, task.getTaskExperience(), task.getTaskIntegral());
            // 标记已发放奖励
            redisTemplate.opsForHash().put(rewardKey, taskKey, "1");
        }
    }

    /**
     * 获取用户当日所有任务进度
     *
     * @param accountId 用户ID
     * @return 各任务进度信息
     */
    public Map<String, Map<String, Object>> getDailyTaskProgress(Integer accountId) {
        Map<String, Map<String, Object>> progress = new HashMap<>();

        // 准备Redis key
        String dailyKey = "user:" + accountId + ":tasks:" + LocalDate.now();
        String rewardKey = "user:" + accountId + ":rewards:" + LocalDate.now();

        // 遍历所有任务类型
        for (Map.Entry<Byte, String> entry : TASK_KEY_MAP.entrySet()) {
            Byte taskType = entry.getKey();
            String taskKey = entry.getValue();

            // 获取任务配置
            Task task = taskMapper.selectTaskConfigByType(taskType);
            if (task == null) continue;

            // 准备单个任务的进度信息
            Map<String, Object> taskProgress = new HashMap<>();

            // 获取当前完成次数
            Integer currentCount = (Integer) redisTemplate.opsForHash().get(dailyKey, taskKey);
            if (currentCount == null) {
                currentCount = 0;
            }

            // 检查是否已领取奖励
            boolean rewarded = redisTemplate.opsForHash().hasKey(rewardKey, taskKey);

            // 填充任务进度信息
            taskProgress.put("taskName", getTaskName(taskType)); // 任务名称
            taskProgress.put("currentCount", currentCount); // 当前完成次数
            taskProgress.put("requiredCount", task.getTaskTime()); // 需求次数
            taskProgress.put("completed", currentCount >= task.getTaskTime()); // 是否完成
            taskProgress.put("rewarded", rewarded); // 是否已领取奖励
            taskProgress.put("experience", task.getTaskExperience()); // 任务经验
            taskProgress.put("points", task.getTaskIntegral()); // 任务积分

            progress.put(taskKey, taskProgress);
        }

        return progress;
    }

    /**
     * 获取任务名称
     *
     * @param taskType 任务类型
     * @return 任务名称
     */
    private String getTaskName(byte taskType) {
        return switch (taskType) {
            case 1 -> "每日签到";
            case 2 -> "发布帖子";
            case 3 -> "发表评论";
            case 4 -> "点赞帖子";
            default -> "未知任务";
        };
    }

    /**
     * 记录用户活跃行为
     *
     * @param accountId 用户ID
     * @param taskType  任务类型
     */
    public void recordActivity(Integer accountId, byte taskType) {
        String today = LocalDate.now().toString();

        //1. 记录每日活跃详情
        String historyKey = String.format(ACTIVITY_HISTORY_KEY, accountId, today);
        redisTemplate.opsForHash().increment(historyKey, TASK_KEY_MAP.get(taskType), 1);

        // 2. 更新活跃度分数
        String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
        int score = TASK_SCORE_MAP.getOrDefault(taskType, 0);
        redisTemplate.opsForZSet().incrementScore(scoreKey, today, score);

        // 3. 更新连续活跃天数
        updateStreakDays(accountId);
    }

    /**
     * 更新连续活跃天数
     *
     * @param accountId 用户ID
     */
    private void updateStreakDays(Integer accountId) {
        String streakKey = String.format(ACTIVITY_STREAK_KEY, accountId); // 连续活跃天数的键
        String yesterday = LocalDate.now().minusDays(1).toString(); // 昨天的日期

        //检查一下昨天是否活跃
        String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
        Double yesterdayScore = redisTemplate.opsForZSet().score(scoreKey, yesterday);
        if (yesterdayScore != null && yesterdayScore > 0) {
            redisTemplate.opsForValue().increment(streakKey); // 增加连续活跃天数
        } else {
            redisTemplate.opsForValue().set(streakKey, 0); // 重置连续活跃天数
        }
    }

    /**
     * 获取用户活跃度
     */
    public Map<String, Object> getUserActivity(Integer accountId, int day) {
        Map<String, Object> result = new HashMap<>();

        //1. 获取近期的活跃数据
        String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(day - 1);

        Set<ZSetOperations.TypedTuple<Object>> allTuples = redisTemplate.opsForZSet()
                .rangeWithScores(scoreKey, 0, -1);
        //2. 统计数据
        int totalScore = 0;
        int activeDays = 0;
        List<Map<String, Object>> dailyActivities = new ArrayList<>();

        assert allTuples != null;
        for (ZSetOperations.TypedTuple<Object> tuple : allTuples) {
            String date = (String) tuple.getValue();
            double score = Objects.requireNonNull(tuple.getScore()).intValue();

            totalScore += (int) score;
            activeDays++;

            //获取当天的活动详情
            String historyKey = String.format(ACTIVITY_HISTORY_KEY, accountId, date);
            Map<Object, Object> dailyActivity = redisTemplate.opsForHash().entries(historyKey);

            Map<String, Object> dayActivity = new HashMap<>();
            dayActivity.put("date", date);
            dayActivity.put("score", score);
            dayActivity.put("details", dailyActivity);
            dailyActivities.add(dayActivity);
        }

        //获取活跃天数
        Integer streak = (Integer) redisTemplate.opsForValue().get(
                String.format(ACTIVITY_STREAK_KEY, accountId));

        // 4. 组装结果
        result.put("totalScore", totalScore); // 总活跃分数
        result.put("averageScore", activeDays > 0 ? totalScore / activeDays : 0); // 平均活跃分数
        result.put("activeDays", activeDays); // 活跃天数
        result.put("streakDays", streak != null ? streak : 0); // 连续活跃天数
        result.put("dailyActivities", dailyActivities); // 每日活跃详情

        return result;
    }


    /**
     * 综合评估用户活跃度是否达标
     *
     * @param accountId 用户ID
     * @return true 表示达标，false 表示不达标
     */
    public double evaluateUserActivity(Integer accountId) {
        // 1. 获取30天内活跃数据
        Map<String, Object> activityData = getUserActivity(accountId, 30);

        // 2. 提取关键指标
        int totalScore = (int) activityData.get("totalScore");    // 总活跃分
        int activeDays = (int) activityData.get("activeDays");   // 活跃天数
        int streakDays = (int) activityData.get("streakDays");    // 连续活跃天数

        // 3. 计算综合评估分（权重：总分60% + 活跃天数30% + 连续天数10%）
        double evaluationScore =
                (totalScore * 0.6) +
                        (activeDays * 0.3) +
                        (streakDays * 0.1);

        // 4. 判断是否达标
        return evaluationScore;
    }

    /**
     * 重置所有用户的任务
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void resetAllTasks() {
        //获取所有用户Id
        List<Integer> accountIds = accountMapper.getAllAccountId();

        for (Integer accountId : accountIds) {
            resetUserTasks(accountId);
        }
    }

    /**
     * 清理过期的数据
     */
    @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
    public void cleanOldActivityData() {
        List<Integer> accountIds = accountMapper.getAllAccountId();
        LocalDate cleanBefore = LocalDate.now().minusDays(30);

        for (Integer accountId : accountIds) {
            String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);

            // 删除30天前的分数记录
            redisTemplate.opsForZSet().removeRangeByScore(
                    scoreKey, 0, cleanBefore.toEpochDay());

            // 删除30天前的详细记录
            for (int i = 31; i <= 60; i++) {
                String oldDate = LocalDate.now().minusDays(i).toString();
                String oldHistoryKey = String.format(ACTIVITY_HISTORY_KEY, accountId, oldDate);
                redisTemplate.delete(oldHistoryKey);
            }
        }
    }

    /**
     * 重置用户任务
     *
     * @param accountId 用户ID
     */
    public void resetUserTasks(Integer accountId) {
        //删除监控状态
        redisTemplate.delete("user:" + accountId + ":monitor");

        //删除前一天的任务记录
        LocalDate yesterday = LocalDate.now().minusDays(1);
        redisTemplate.delete("user:" + accountId + ":tasks:" + LocalDate.now().minusDays(1));
        redisTemplate.delete("user:" + accountId + ":rewards:" + LocalDate.now().minusDays(1));

        // 检查昨天是否活跃，如果活跃则保留活跃度记录
        String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
        Double yesterdayScore = redisTemplate.opsForZSet().score(scoreKey, yesterday.toString());
        if (yesterdayScore == null || yesterdayScore <= 0) {
            // 如果昨天不活跃，重置连续活跃天数
            redisTemplate.opsForValue().set(
                    String.format(ACTIVITY_STREAK_KEY, accountId), 0);
        }
    }


    /**
     * 为指定用户添加模拟活跃数据以达到产品体验官评估值(260分)
     *
     * @param accountId 用户ID
     * @return 操作结果信息
     */
    public Map<String, Object> addMockActivityDataWithDebugInfo(Integer accountId) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 1. 清除旧数据
            String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
            String streakKey = String.format(ACTIVITY_STREAK_KEY, accountId);
            redisTemplate.delete(scoreKey);
            redisTemplate.delete(streakKey);

            // 2. 计算需要的每日分数 (260 = (totalScore*0.6 + activeDays*0.3 + streakDays*0.1))
            // 假设30天都活跃，连续活跃30天
            // 260 = (x*30 * 0.6 + 30 * 0.3 + 30 * 0.1)
            // 260 = 18x + 9 + 3 => 18x = 248 => x ≈ 13.78
            // 所以我们每天需要14分
            int dailyScore = 14;
            int days = 30;

            // 3. 添加30天的活跃数据
            LocalDate today = LocalDate.now();
            List<Map<String, Object>> scoreDetails = new ArrayList<>();

            for (int i = 0; i < days; i++) {
                LocalDate date = today.minusDays(i);
                String dateStr = date.toString();

                // 添加活跃度分数
                redisTemplate.opsForZSet().add(scoreKey, dateStr, dailyScore);

                // 添加详细活动记录（模拟签到+发帖）
                String historyKey = String.format(ACTIVITY_HISTORY_KEY, accountId, dateStr);
                redisTemplate.opsForHash().put(historyKey, "sign_in", "1");
                redisTemplate.opsForHash().put(historyKey, "post", "1");

                // 记录添加的数据
                scoreDetails.add(Map.of(
                        "date", dateStr,
                        "score", dailyScore
                ));
            }

            // 4. 设置连续活跃天数为30
            redisTemplate.opsForValue().set(streakKey, days);

            // 5. 验证评估结果
            double evaluationResult = evaluateUserActivity(accountId);
            Map<String, Object> activityData = getUserActivity(accountId, days);

            // 6. 组装返回结果
            result.put("status", "success");
            result.put("accountId", accountId);
            result.put("addedScores", scoreDetails);
            result.put("streakDays", days);
            result.put("evaluationResult", evaluationResult);
            result.put("evaluationData", activityData);

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }

    /**
     * 为指定用户添加模拟活跃数据以达到版主评估值(310分)
     *
     * @param accountId 用户ID
     * @return 操作结果信息
     */
    public Map<String, Object> addMockActivityDataForModerator(Integer accountId) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 1. 清除旧数据
            String scoreKey = String.format(ACTIVITY_SCORE_KEY, accountId);
            String streakKey = String.format(ACTIVITY_STREAK_KEY, accountId);
            redisTemplate.delete(scoreKey);
            redisTemplate.delete(streakKey);

            // 2. 计算需要的每日分数 (310 = (totalScore*0.6 + activeDays*0.3 + streakDays*0.1))
            // 假设30天都活跃，连续活跃30天
            // 310 = (x*30 * 0.6 + 30 * 0.3 + 30 * 0.1)
            // 310 = 18x + 9 + 3 => 18x = 298 => x ≈ 16.56
            // 所以我们每天需要17分
            int dailyScore = 17;
            int days = 30;

            // 3. 添加30天的活跃数据
            LocalDate today = LocalDate.now();
            List<Map<String, Object>> scoreDetails = new ArrayList<>();

            for (int i = 0; i < days; i++) {
                LocalDate date = today.minusDays(i);
                String dateStr = date.toString();

                // 添加活跃度分数
                redisTemplate.opsForZSet().add(scoreKey, dateStr, dailyScore);

                // 添加详细活动记录（模拟签到+发帖+评论）
                String historyKey = String.format(ACTIVITY_HISTORY_KEY, accountId, dateStr);
                redisTemplate.opsForHash().put(historyKey, "sign_in", "1");
                redisTemplate.opsForHash().put(historyKey, "post", "1");
                redisTemplate.opsForHash().put(historyKey, "comment", "2");

                // 记录添加的数据
                scoreDetails.add(Map.of(
                        "date", dateStr,
                        "score", dailyScore
                ));
            }

            // 4. 设置连续活跃天数为30
            redisTemplate.opsForValue().set(streakKey, days);

            // 5. 验证评估结果
            double evaluationResult = evaluateUserActivity(accountId);
            Map<String, Object> activityData = getUserActivity(accountId, days);

            // 6. 组装返回结果
            result.put("status", "success");
            result.put("accountId", accountId);
            result.put("addedScores", scoreDetails);
            result.put("streakDays", days);
            result.put("evaluationResult", evaluationResult);
            result.put("evaluationData", activityData);

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }
}
